//=============================================================================
//
// StopGoBtButtonPart.cpp : "Stop and go via Bluetooth (button part)"
//
// This example starts and stops motor, connected to outputs M1 on other 
// ROBO TX Controller, by means of the button connected to the input I8.
// Pulses from the motor are calculated by the counter C1 on other
// ROBO TX Controller. The motor is stopped after the counter reaches
// the value of 1000.
//
//-----------------------------------------------------------------------------
// Disclaimer - Exclusion of Liability
//
// This software is distributed in the hope that it will be useful,but WITHOUT 
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
// FITNESS FOR A PARTICULAR PURPOSE. It can be used an modified by anyone
// free of any license obligations or authoring rights.
//=============================================================================

#include "StdAfx.h"

#define MOTOR_NUMBER    1
#define BUTTON_NUMBER   8
#define BUTTON_IDX      (BUTTON_NUMBER - 1)

#define BT_CHANNEL      1

#define RECV_TIMEOUT    50  // milliseconds

//  Bluetooth address of other ROBO TX Controller
static BYTE * bt_address = bt_address_table[1];

#define STRMAXLEN       80

static HANDLE           fthdl;
static DWORD            errCode;
static char             ComPortName[STRMAXLEN];
static char             LibVersion[STRMAXLEN];

static INT16 prev_button_state;
static INT16 remote_counter_value;
static enum bt_commands_e command;
static volatile CHAR8 command_status;
static volatile CHAR8 receive_command_status;
static bool was_receive;
static bool stop;


//=============================================================================

/*-----------------------------------------------------------------------------
 * Function Name       : BtCallback
 *
 * This callback function is called to inform the program about result (status)
 * of execution of any Bluetooth command except BtReadMsgOn command.
 *-----------------------------------------------------------------------------*/
static void __stdcall BtCallback(BT_CB *p_data) {

    command_status = (CHAR8)p_data->status;
}

/*-----------------------------------------------------------------------------
 * Function Name       : BtReceiveCallback
 *
 * This callback function is called to inform the program about result (status)
 * of execution of BtReadMsgOn command. It is also called when a message
 * arrives via Bluetooth.
 *-----------------------------------------------------------------------------*/
static void __stdcall BtReceiveCallback(BT_RECV_CB * p_data) {

    was_receive = TRUE;

    if (p_data->status == BT_MSG_INDICATION) {

        UCHAR8 counter;

        // Format of a received message should be:
        // byte 0  : counter number(1...N_CNT)
        // byte 1-2: counter value(0...0xFFFF)
        counter = p_data->msg[0];
        if (counter >= 1 && counter <= N_CNT) {
            memcpy(&remote_counter_value, &p_data->msg[1], sizeof(remote_counter_value));
        }
    }
    else {
        receive_command_status = (CHAR8)p_data->status;
    }
}

/*-----------------------------------------------------------------------------
 *  StopGoBtButtonPart  
 *---------------------------------------------------------------------------*/
static void StopGoBtButtonPart(void) {

    //  configure input I8 to "Digital 5 kOhm" mode
    SetFtUniConfig(fthdl, TA_LOCAL, BUTTON_IDX, MODE_R, TRUE);

    //  emulate button press in order to run at program start-up on
    //  program branch where the button release is noticed
    prev_button_state = 1;

    remote_counter_value = 0;
    stop = FALSE;

    //  connect to the controller with bt_address via Bluetooth channel BT_CHANNEL
    command = CMD_CONNECT;
    command_status = -1;
    ConnectBtAddress(fthdl, BT_CHANNEL, bt_address, BtCallback);

    while (command_status < 0) Sleep(1);
    BtDisplayCommandStatus(bt_address, BT_CHANNEL, command, command_status);
    if (command_status != BT_SUCCESS)
        return;

    //  start receive from Bluetooth channel BT_CHANNEL
    command = CMD_START_RECEIVE;
    receive_command_status = -1;
    BtReadMsgOn(fthdl, BT_CHANNEL, BtReceiveCallback);

    while (receive_command_status < 0) Sleep(1);
    BtDisplayCommandStatus(bt_address, BT_CHANNEL, command, receive_command_status);
    if (receive_command_status != BT_SUCCESS)
        return;

    //  pointer all Transfer Areas
    volatile TA_ARRAY *IfTransfer = GetTransferAreasArrayAddr(fthdl);

    //  pointer Transfer Area of local interface
    volatile TA *pTA = &IfTransfer->ftxTransferArea[TA_LOCAL];

    //  pointer Timer values
    volatile TA_TIMER *pTimer = &pTA->timer;

    int timeout = pTimer->Timer1ms + RECV_TIMEOUT;

    while (!stop) {

        if (was_receive || command_status >= 0 || pTimer->Timer1ms > timeout) {

            if (command_status >= 0 && command_status != BT_SUCCESS) {

                //  usually if we have come here, then this means the other controller
                //  has disconnected from us
                BtDisplayCommandStatus(bt_address, BT_CHANNEL, command, command_status);
                return;
            }
            else {

                UCHAR8 msg[3];
                INT16 duty;
                INT16 cur_button_state;
                BOOL32 overrun;


                was_receive = FALSE;

                //  read current status of the button input
                GetInIOValue(fthdl, TA_LOCAL, BUTTON_IDX, &cur_button_state, &overrun);

                //  start motor if button on input I8 is pressed, otherwise stop it
                duty = (cur_button_state) ? DUTY_MAX : 0;

                //  program should be executed until counter reaches 1000
                if (remote_counter_value >= 1000) {
                    printf("Motor M%d reached position 1000\n", MOTOR_NUMBER);
                    duty = 0;
                    stop = TRUE;
                }
                else if (prev_button_state != cur_button_state) {

                    if (prev_button_state && !cur_button_state) { // if button was released
                        printf("Press button I%d to run motor M%d on other TXC\n",
                            BUTTON_NUMBER, MOTOR_NUMBER);
                    }
                    prev_button_state = cur_button_state;
                }

                //  prepare BT message
                msg[0] = MOTOR_NUMBER;                // motor number
                memcpy(&msg[1], &duty, sizeof(duty)); // motor duty

                //  send BT message
                command = CMD_SEND;
                command_status = -1;
                SendBtMessage(fthdl, BT_CHANNEL, sizeof(msg), (LPSTR)msg, BtCallback);

                while (command_status < 0) Sleep(1);
                if (command_status != BT_SUCCESS) {
                    BtDisplayCommandStatus(bt_address, BT_CHANNEL, command, command_status);
                    return;
                }

                command = CMD_NO_CMD;
                command_status = -1;
                timeout = pTimer->Timer1ms + RECV_TIMEOUT;
            }
        }
        Sleep(10);
    }
}

/*-----------------------------------------------------------------------------
 *  CheckParameter  
 *---------------------------------------------------------------------------*/
static int CheckCOMPar(int argc, char *argv[]) {

    char    *pStr;
    int     comNo;

    if (argc >= 2) {
        if (strlen(argv[1]) > 3) {
            if ((pStr=strstr(argv[1],"COM")) != NULL) {
                sscanf(pStr+3, "%d", &comNo);
                if (comNo >= 1 && comNo <= 255)
                    return 0;
                else {
                    cout << "StopGoBtButtonPart.exe: invalid COM number..." << endl << endl;
                    return 1;
                }
            }
        }
    }

    cout << "StopGoBtButtonPart.exe: no input given..." << endl << endl;
    return 1;
}

/*-----------------------------------------------------------------------------
 *  main
 *  
 *---------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {

    cout << "\nExample StopGoBtButtonPart.exe ..." << endl;

    //  check input paramter
    if (CheckCOMPar(argc,argv)) {
        cout << "Usage: StopGoBtButtonPart.exe COMxx\t(e.g. COM2 or COM32)" << endl;
        return 1;
    }

    //  get library version
    ftxGetLibVersionStr(LibVersion, STRMAXLEN);
    cout << "\nftMscLib " << LibVersion << endl;

    //  library initialization
    errCode = ftxInitLib();

    strcpy(ComPortName, argv[1]);
    cout << "\nOpen ComPort '" << ComPortName << "' ..." << endl;

    //  open COM port
    fthdl = ftxOpenComDevice(ComPortName, 38400, &errCode);

    if (errCode == FTLIB_ERR_SUCCESS) {

        cout << "Connected to ROBO TX Controller ..." << endl;

        //  starting Transfer Area
        errCode = ftxStartTransferArea(fthdl);

        if (errCode == FTLIB_ERR_SUCCESS) {

            cout << "Transfer Area was started and runs..." << endl;

            StopGoBtButtonPart();

            //  stop Transfer Area
            ftxStopTransferArea(fthdl);
        }

        else {
            //  error case
            cout << "Error: Transfer Area was not started !" << endl;
        }

        //  closing port
        cout << "Closing ComPort '" << ComPortName << "' ..." << endl;
        errCode = ftxCloseDevice(fthdl);
    }

    else {
        //  error case
        cout << "Error: No interface available (Port '" << ComPortName << "')" << endl;
    }

    //  close library
    ftxCloseLib();

    return 0;
}
